﻿using ImCommon;
using System;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;

namespace ImClient {
    // 客户端
    class Client: Socket {
        public delegate void ON_CHAT_S2C(ChatInfo chat);
        public delegate void ON_FRIEND_LOGOFF(LogoffRequest request);
        public delegate void ON_FRIEND_LOGON(FriendLogon friendLogon);
        public delegate void ON_GET_ADD_FRIEND_REQUEST(AddFriendRequest request);
        public delegate void ON_GET_ADD_FRIEND_RESULT(AddFriendResult result);
        public delegate void ON_GET_LOGON_RESULT(LogonResult result);
        public delegate void ON_GET_REG_RESULT(RegResult result);

        public ON_CHAT_S2C OnChatS2C;                           // 聊天事件（服务器到客户端）
        public ON_FRIEND_LOGOFF OnFriendLogoff;                 // 好友离线事件
        public ON_FRIEND_LOGON OnFriendLogon;                   // 好友登录事件
        public ON_GET_ADD_FRIEND_REQUEST OnGetAddFriendRequest; // 得到添加好友请求事件
        public ON_GET_ADD_FRIEND_RESULT OnGetAddFriendResult;   // 得到添加好友结果事件
        public ON_GET_LOGON_RESULT OnGetLogonResult;            // 得到登录结果事件
        public ON_GET_REG_RESULT OnGetRegResult;                // 得到注册结果事件

        public int FriendCount { get; private set; }        // 好友数
        public static string UserName { get; private set; } // 用户名
        private readonly Control mCallback;                 // 回调
        private int mIconIndex;                             // 头像索引

        /**
         * 构造方法。
         * @param callback  回调
         */
        public Client(Control callback): 
            base(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) {
            // 初始化成员
            FriendCount = 0;
            UserName = "";
            mCallback = callback;
            mIconIndex = 0;

            // 连接服务器
            base.Connect(ImProtocol.SERVER_IP, ImProtocol.SERVER_PORT);

            // 创建接收数据的线程
            new Thread(ReceiveData).Start();
        }
        
        /**
         * 添加好友。
         * @param friendName 好友名字
         * @return 结果
         */
        public ResultCode AddFriend(string friendName) {
            // 检查是否超过好友上限
            if (FriendCount > Const.MAX_USER_COUNT) {
                return ResultCode.TOO_MANY_FRIENDS;
            }

            // 检查好友名
            if (friendName.Equals(UserName)) {
                return ResultCode.FRIEND_NAME_EQUAL_SELF_NAME;
            }

            // 发送添加好友请求
            AddFriendRequest request = new AddFriendRequest(friendName, mIconIndex, UserName);
            this.SendData(request);

            return ResultCode.SUCCEEDED;
        }
        
        /**
         * 允许或拒绝添加好友。
         * @param allow     允许则为 true，否则为 false
         * @param request   添加好友请求
         */
        public void AllowAddFriend(bool allow, AddFriendRequest request) {
            // 发送结果
            AddFriendResult addFriendResult = new AddFriendResult(mIconIndex, request.mDstUsername, 
                allow ? ResultCode.ADD_FRIEND_ALLOW: ResultCode.ADD_FRIEND_NOT_ALLOW, request.mSrcUsername);
            this.SendData(addFriendResult);

            // 如果允许，则好友数加1
            if (allow) {
                FriendCount++;
            }
        }

        /**
         * 聊天。
         * @param chatInfo  聊天信息
         */
        public void Chat(ChatInfo chatInfo) {
            this.SendData(chatInfo);
        }

        /**
         * 关闭客户端。
         */
        public new void Close() {
            // 离线
            this.Logoff();

            // 延迟一下，防止数据还没发送完成就关闭网络
            Thread.Sleep(100);

            // 关闭
            base.Close();
        }

        /**
         * 处理添加好友请求。
         * @param request   添加好友请求
         */
        private void HandleAddFriendRequest(AddFriendRequest request) {
            mCallback.Invoke(OnGetAddFriendRequest, request);
        }

        /**
         * 处理添加好友结果。
         * @param result    添加好友结果
         */
        private void HandleAddFriendResult(AddFriendResult result) {
            if (result.mResultCode == ResultCode.ADD_FRIEND_ALLOW && result.mDstIconIndex != -1) {
                FriendCount++;
            }

            mCallback.Invoke(OnGetAddFriendResult, result);
        }

        /**
         * 处理聊天。
         * @param chat  聊天信息
         */
        private void HandleChat(ChatInfo chat) {
            mCallback.Invoke(OnChatS2C, chat);
        }

        /**
         * 处理其他用户的离线请求。
         * @param request   其他用户的离线请求
         */
        private void HandleFriendLogoff(LogoffRequest request) {
            mCallback.Invoke(OnFriendLogoff, request);
        }

        /**
         * 处理其他用户的登录消息。
         * @param friendLogon   其他用户的登录消息
         */
        private void HandleFriendLogon(FriendLogon friendLogon) {
            mCallback.Invoke(OnFriendLogon, friendLogon);
        }
 
        /**
         * 处理登录结果。
         * @param result    登录结果
         */
        private void HandleLogonResult(LogonResult result) {
            mCallback.Invoke(OnGetLogonResult, result);

            if (result.mResultCode == ResultCode.LOGON_SUCCEEDED) {
                mIconIndex = result.mIconIndex;
                FriendCount = result.mFriendCount;
            } else {
                UserName = "";
            }
        }

        /**
         * 处理注册结果。
         * @param result    注册结果
         */
        private void HandleRegResult(RegResult result) {
            mCallback.Invoke(OnGetRegResult, result);

            if (result.mResultCode != ResultCode.REG_SUCCEEDED) {
                UserName = "";
            }
        }

        /**
         * 离线。
         */
        public void Logoff() {
            if (UserName.Length == 0) {
                return;
            }

            LogoffRequest request = new LogoffRequest(UserName);
            this.SendData(request);

            UserName = "";
        }

        /**
         * 登录。
         * @param username  用户名
         * @param password  密码
         */
        public void Logon(string username, string password) {
            // 发送登录请求
            LogonRequest request = new LogonRequest(password, username);
            this.SendData(request);

            // 保存用户名
            UserName = username;;
        }

        /**
         * 处理服务器发送来的数据。
         * @param pro   协议
         */
        private void OnGetMessageFromServer(ImProtocol pro) {
            // 根据协议类型做相应处理
            switch (pro.mType) {
                case ImProtocol.Type.ADD_FRIEND_REQUEST:
                    this.HandleAddFriendRequest((AddFriendRequest)pro);
                    break;
                case ImProtocol.Type.ADD_FRIEND_RESULT:
                    this.HandleAddFriendResult((AddFriendResult)pro);
                    break;
                case ImProtocol.Type.CHAT:
                    this.HandleChat((ChatInfo)pro);
                    break;
                case ImProtocol.Type.FRIEND_LOGON:
                    this.HandleFriendLogon((FriendLogon)pro);
                    break;
                case ImProtocol.Type.LOGOFF:
                    this.HandleFriendLogoff((LogoffRequest)pro);
                    break;
                case ImProtocol.Type.LOGON:
                    this.HandleLogonResult((LogonResult)pro);
                    break;
                case ImProtocol.Type.REG:
                    this.HandleRegResult((RegResult)pro);
                    break;
            }
        }

        /**
         * 接收数据。
         */
        private void ReceiveData() {
            int dataSize = 0;   // 数据长度
            byte[] buffer;      // 接收数据的缓存
            int read = 1;       // 接收到的数据长度
            ImProtocol pro;     // 协议

            while (read > 0) {
                // 接收数据长度
                buffer = new byte[sizeof(int)];
                try {
                    read = base.Receive(buffer, sizeof(ImProtocol.Type), SocketFlags.None);
                } catch (Exception) {
                    break;
                }
                if (read < sizeof(ImProtocol.Type)) {
                    break;
                }
                dataSize = BitConverter.ToInt32(buffer, 0);

                // 接收数据
                buffer = new byte[dataSize];
                read = base.Receive(buffer, dataSize, SocketFlags.None);
                if (read < sizeof(ImProtocol.Type)) {
                    break;
                }

                // 将数据转换成协议
                pro = ImProtocol.FromBytes(buffer);

                // 处理协议
                this.OnGetMessageFromServer(pro);
            }
        }

        /**
         * 注册。
         * @param username  用户名
         * @param password  密码
         * @param iconIndex 头像索引
         */
        public void Reg(string username, string password, int iconIndex) {
            // 发送注册请求
            RegRequest request = new RegRequest(iconIndex, password, username);
            this.SendData(request);

            // 保存用户名和头像索引
            UserName = username;
            mIconIndex = iconIndex;
        }

        /**
         * 发送数据。
         * @param pro   协议
         */
        private void SendData(ImProtocol pro) {
            // 将协议转换成数据
            byte[] data = ImProtocol.ToBytes(pro);

            // 发送数据
            base.Send(data);

            // 防止发送过快导致服务器无法响应
            Thread.Sleep(100);
        }
    }
}
